错误分类

  1. 即时运行错误(代码错误)也就是 JS 异常
  2. 网络异常
  3. 资源加载错误(静态资源加载失败)

捕获方式

1. 即时运行错误的捕获方式

  1. try ... catch

这种方式要部署在代码中。

  1. window.onerror函数。这个函数是全局的。window.onerror是属于 DOM0 的写法,我们也可以用 DOM2 的写法:window.addEventListener("error", fn);也可以。 这个只能捕获即时运行错误,资源加载错误因为不会冒泡,所以不会被 window.onerror 捕获到。在 IE 中,是不支持的,所以 IE 的监控,要使用 try catch 的方式进行捕获
	window.onerror = function(msg, url, row, col, error) { ... }

参数解释:

  • msg为异常基本信息
  • source为发生异常Javascript文件的url
  • row为发生错误的行号
跨域的 JS 运行错误

因为只知道错误,但是无法定位错误信息

解决方法:

  1. 客户端,在 script 标签上增加 crossorigin 属性
  2. 服务端,设置 JS 资源响应头 Access-Control-Allow-Origin: *

上报错误的基本原理

  1. 采用 ajax 上报
  2. 利用 Image src 上报 (new Image ()).src = "xxxx"

问题延伸 1:

window.onerror默认无法捕获跨域js运行错误。捕获出来的信息如下:(基本属于无效信息)

比如说,我们的代码想引入B网站的b.js文件,怎么捕获它的异常呢?

解决办法:在方法二的基础之上,做如下操作:

  1. b.js文件里,加入如下 response header,表示允许跨域:(或者世界给静态资源b.js加这个 response header)
	Access-Control-Allow-Origin: *
  1. 引入第三方的文件b.js时,在<script>标签中增加crossorigin属性;

问题延伸 2:

只靠方式 2 中的window.onerror是不够的,因为我们无法获取文件名是什么,不知道哪里出了错误。解决办法:把堆栈信息作为 msg 打印出来,堆栈里很详细。

2. 网络异常自动捕获

通过错误码的判断。

所有你不想让用户接手的错误处理都应该用。通常在以下几点使用:

  • 复杂逻辑代码库
  • 发起 ajax、fetch 的时候
  • 判断是否支持默写浏览器特性

3. 资源加载错误

上面的window.onerror只能捕获即时运行错误,无法捕获资源加载错误。原理是:资源加载错误,并不会向上冒泡,object.onerror捕获后就会终止(不会冒泡给window),所以window.onerror并不能捕获资源加载错误。

  1. object.onerror 比如说是一个 img 标签,这里就是 img 的 onerror 事件,就可以捕获到, script 标签也是。资源加载错误不会冒泡,但会捕获。
  2. performance.getEntries 高级浏览器才有。这个 api 可以获取到所有已经加载资源的加载时长,可以间接获取到未加载的资源。
  3. Error 事件捕获 window.addEventListener('error', function(){}, true) 只是不能冒泡,但是捕获是可以的。
performance.getEntries().forEach(({ name, duration }) => {
  console.log(name, duration);
});

上面这个api,返回的是数组,既然是数组,就可以用forEach遍历。打印出来的资源就是已经成功加载的资源。;

再入document.getElementsByTagName('img'),就会显示出所有需要加载的的 img 集合。

于是,document.getElementsByTagName('img')获取的资源数组减去通过performance.getEntries()获取的资源数组,剩下的就是没有成功加载的,这种方式可以间接捕获到资源加载错误。

这种方式非常有用,一定要记住。

**方式 3;**Error 事件捕获。

源加载错误,虽然会阻止冒泡,但是不会阻止捕获。我们可以在捕获阶段绑定 error 事件。例如:

**总结:**如果我们能回答出后面的两种方式,面试官对我们的印象会大大增加。既可以体现出我们对错误监控的了解,还可以体现出我们对事件模型的掌握

4. try catch

try 语句执行一个代码块,并捕获该代码块抛出的异常。catch 从句定义了一个新的变量,它将接收该异常。 throw 语句抛出一个异常,如果 throw 语句在一个 try 代码块中,那么控制权会跳到 catch 从句中。如果 throw 语句在函数中,则该函数调用被抛弃,且控制权会跳到调用该函数的 try 语句的 catch 从句中。 throw 语句中的表达式通常是一个对象字面量,它包含一个 name 属性和 message 属性。异常捕获器可以使用这些信息区决定该做什么。??

5. 逻辑中的错误判断

主动上报

上报方式

1. 采用 Ajax 通信的方式上报

axios 上报也有的,此方式虽然可以上报错误,但是我们并不采用这种方式。

2. img 标签的 src 属性

推荐。网站的监控体系都是采用的这种方式

目前主流的上报方式是利用 img 标签的 src 属性发送请求。 因为日志上报不需要响应处理,只需要把数据发过去就行。并且大部分接收日志的服务器地址与业务方可能不是一个部门,甚至可能不是一个公司,所以会涉及到跨域问题。使用 img 标签的 src 属性既可以把数据发送给服务端又不需要接收响应,同时解决了跨域问题,所以是目前比较受欢迎的日志上报实现方式。

这种方式,不需要借助第三方的库,一行代码即可搞定。

//通过 Image对象进行错误上报
new Image().src = "http://smyhvae.com/myPath?badjs=msg"; // myPath表示上报的路径(我要上报到哪里去)。后面的内容是自己加的参数。

有很多优点:

  1. 跨域
  2. 不会阻塞页面
  3. 所有图片中的体积最小

使用 NEW IMAGE()上报数据的一些问题

日常前台开发少不了数据上报,一般都是通过 js 发起一个请求到服务器程序,比如 CGI、PHP、jsP 程序等,由于只是上报,只要把数据上报了就行,并不关心服务器的状态和返回值,我们一般是使用 new Image()对象,然后设置这个对象 src 属性。

function report() {
  var img = new Image();
  img.src = "http://www.example.com/getReport.php";
}

我们一般是把上面的代码封装到一个函数里,每次使用的时候调用这个函数就可以了,看起来挺好的,用起来也挺方便,但是问题来了:在某次 JS 动态拉某服务器资源的时候,我们在服务器做了统计,同时也用 JS 做了上报,但是统计的结果是,JS 统计的总比服务端统计的少 30%,天天如此,这个数据差值总是 30%。当时查了几天都没有结果,后来猜测可能是这里的临时变量问题导致,把这个变量移植到 window 对象下作为全局变量,问题解决。

var img;
function report() {
  img = new Image();
  img.src = "http://www.example.com/getRepor.php";
}

究竟是什么原因导致这个问题呢?

分析是这样的,在函数 report ()里,局部变量 img 设置属性 src 以后,浏览器并不是立即就发起请求,或者请求准备发起但是还没有真正发起(回忆下网络请求的几个状态值),而函数 report 执行完毕,浏览器开始做垃圾回收工作,包括局部变量,上报请求还没有开始 img 就被回收了,那么这次的上报就被取消了,上报失败。而把 img 放到 window 对象下作为全局变量,则避免了这个问题,所以即使 report()函数的所有资源被回收,也不会影响到其上报

为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片

  1. 能够完成整个 HTTP 请求+响应(尽管不需要响应内容)
  2. 触发 GET 请求之后不需要获取和处理数据、服务器也不需要发送数据
  3. 跨域友好
  4. 执行过程无阻塞
  5. 相比 XMLHttpRequest 对象发送 GET 请求,性能上更好
  6. GIF 的最低合法体积最小(最小的 BMP 文件需要 74 个字节,PNG 需要 67 个字节,而合法的 GIF,只需要 43 个字节)

作用:工作中,用于前端监控,比如曝光等等,谷歌和百度的都是用的 1x1 像素的透明 gif 图片; why?

没有跨域问题,一般这种上报数据,代码要写通用的;(排除 ajax) 不会阻塞页面加载,影响用户的体验,只要 new Image 对象就好了;(排除 JS/CSS 文件资源方式上报) 在所有图片中,体积最小;(比较 PNG/JPG)

3. Beacon

新 api navigator.beacon 能够在页面关闭之前在请求执行完毕,降低打点的丢失率。

日志上报并不是应用的主要功能逻辑,也就是说,日志上报是低优先级的,它不应该与其他高优先级操作(例如:获取关键资源、输入响应、运行动画等)去竞争网络与计算资源(通俗的说就是日志上报行为不应该影响业务逻辑,不应该占用业务计算资源)。但是这种单向请求又负责传递应用的错误与性能数据,所以我们必须要确保它会被交付到服务端。

通常,为了提高交付率,我们会选择立即交付每个收集到的数据,而不是合并与推迟交付。延迟传递可能意味着请求没有足够的时间来成功完成,这可能导致重要的应用数据丢失。

这就意味着我们的交付行为有可能会被插入到正在忙碌工作的事件循环中,从而抢占了其他高优先级的任务的资源,因为 JS 是单线程的。这有可能会损害用户体验。

我们如何确保日志数据会被交付的同时,尽可能地减少与其他关键操作的资源争用呢?答案是信标(Beacon)。

监控

前端监控一般分为三种,分别为页面埋点、性能监控以及异常监控。

1. 页面埋点

页面埋点应该是大家最常写的监控了,一般起码会监控以下几个数据:

  • PV / UV
  • 停留时长
  • 流量来源 from/reference 字段
  • 用户交互

对于这几类统计,一般的实现思路大致可以分为两种,分别为手写埋点和无埋点的方式。

第一种方式也是大家最常用的方式,可以自主选择需要监控的数据然后在相应的地方写入代码。这种方式的灵活性很大,但是唯一的缺点就是工作量较大,每个需要监控的地方都得插入代码。另一种无埋点的方式基本不需要开发者手写埋点了,而是统计所有的事件并且定时上报。这种方式虽然没有前一种方式繁琐了,但是因为统计的是所有事件,所以还需要后期过滤出需要的数据。

2. 性能监控

性能监控可以很好的帮助开发者了解在各种真实环境下,页面的性能情况是如何的。对于性能监控来说,只需要调用 performance.getEntriesByType('navigation') 就可以获得页面中各种详细的性能相关信息。我们可以发现这行代码返回了一个数组,内部包含了相当多的信息,从数据开始在网络中传输到页面加载完成都提供了相应的数据。

性能优化指标

有什么指标

首屏时间、加载时间。实际上都不太标准,所以定义为一个核心功能可用时间。

记录 performance 以及 onload 的加载时间。

performance 属性的字段。

  • http://www.cnblogs.com/sunshq/p/5312231.html
  • https://www.cnblogs.com/libin-1/p/6501951.html

关于时间计算: html5 新增的接口 performance 白屏时间计算 ?

performance.timing.navigationStart 表示一个开始时间。 白屏时间的计算,是从开始加载到底部的 script 标签都执行结束之后的整个过程。 用户可操作性时间,是 DOMContentLoaded 事件执行结束之后。 下载总时间: onload 事件执行之后

3. 异常监控

对于异常监控来说,以下两种监控是必不可少的,分别是代码报错以及接口异常上报。

对于代码运行错误,通常的办法是使用 window.onerror 拦截报错。该方法能拦截到大部分的详细报错信息,但是也有例外

  • 对于跨域的代码运行错误会显示 Script error. 对于这种情况我们需要给 script 标签添加 crossorigin 属性
  • 对于某些浏览器可能不会显示调用栈信息,这种情况可以通过 arguments.callee.caller 来做栈递归

对于异步代码来说,可以使用 catch 的方式捕获错误。比如 Promise 可以直接使用 catch 函数,async await 可以使用 try catch

但是要注意线上运行的代码都是压缩过的,需要在打包时生成 sourceMap 文件便于 debug。

对于捕获的错误需要上传给服务器,通常可以通过 img 标签的 src 发起一个请求。

另外接口异常就相对来说简单了,可以列举出出错的状态码。一旦出现此类的状态码就可以立即上报出错。接口异常上报可以让开发人员迅速知道有哪些接口出现了大面积的报错,以便迅速修复问题。

统计类型

  • js error
  • console.error
  • ajax
  • promise err catch: unhandrejection ?
  • 统计打点的时间

一个监控关注的内容:

  • performance
    • 首屏
    • dns 时间等
  • js 异常
    • ???
  • console.error 异常
    • 重载 console.error
  • ajax 异常
    • 重写原生 ajax
  • 普通信息上报
    • 普通上报携带一个 type

监控搭建

背景是什么,为什么要做,期望的成果如何,现在做到什么阶段了,遇到过什么问题?

  1. 错误日志、性能信息收集
  2. 日志清洗、计算、出库
  3. 平台可视化展示信息
  4. 告警、报告推送
  5. 异常收集,有堆栈信息 sourcemap 收集和解析
  6. cgi 的耗时,异常通知
  7. 首屏加载耗时
  8. performance 统计分析
  9. 自定义打点上报
  10. 实时监控(针对一部分 uin 或者 rtx)
  11. 自定义上报源 (可以选择上报数据到自己的接口去,自己去分析)
  12. 告警(rtx 邮件 微信)
  13. 可视化分析部分

一个完善的前端框架,一般需要具备以下的几个功能:

  1. 数据上报, 日志埋点, pv
  2. 错误告警、上报
  3. 合理的日志系统,不同的级别日志

错误监控上报平台(收集上报、数据可视化、监控)如何考虑埋点,取多少数据?如何进行页面级跟踪,还原操作步骤,打点恢复浏览内容

  • 错误上报
  • 主动上报
  • cgi 耗时监控等
  • 日志链路

优化

  • 效率: 合并打点。 搞一个简单的数组

可用性监控

要做有效的前端监控,每天、产品线的错误都需要去看

JSError 的分类。 不能超过万 5 这个标准, 超过的都需要去解决。

如果没有一个衡量标准的话,就没有很好的推动性。

核心功能的三秒可用 ? 页面加载速度提高,减少客户的流失。

前端安全生产

安全问题如何进行高效复盘? => 每一个安全问题都需要被复盘,被认真对待,以小见大。

特殊场景

  1. 语义对话过程,全链路都是 200 正常,但是语义测返回的内容不符合要求,也算是异常
  2. 老板打开页面的时候,需要实时监控。

前端如何做性能监控、异常监控?

性能监控,异常监控,基本在小公司,是没有实践基础的,可是在差不多的大厂中,他们会关注这个问题。 首先是性能监控,应该从这么几个维度来说:一个是 http 的方面,在后端 log 日志,流入 kafka,然后在 kafka 消费数据,可以准确的监控到哪些接口有异常?异常率是多少? 另一个方面,是前端的 Performance 的 api,在用户的实时使用的过程中,就会产生数据,这样就能实现页面性能监控。 前端异常监控,首先要明白什么是异常,html、css 这些东西,无非就是一个展示的问题,还不至于让页面白屏的事情发生,所谓的异常监控,其实就是 js 的异常监控。在前端领域,window.onerror 是进行 js 异常的监听事件。并且要知道,它在 IE 中,是不支持的,所以 IE 的监控,要使用 try catch 的方式进行捕获,比如我们可能还要注意到,遇到异步的时候,这个如何做 try catch 的异常捕获。 最后一个是前端 sdk 埋点,直接开发一个 js 文件,统计用户的 UV/PV 分析等等,比如用户的转化率之类的,这一块个人没有什么特别的实践,各位可以在网上百度看看。

其他需要考虑的点

  • 前端异常监测如何实现
  • 白屏、首屏的时间计算
  • CGI 的健康程度 ???
  • 日志上报
    • 不影响性能
    • 如何设计埋点
  • 接口请求无痕埋点思路 https://www.jianshu.com/p/467544cb088e
  • sentry https://segmentfault.com/a/1190000014496409 上传 sourcemap 版本号等信息。 error 会被吃掉

前端如何实现 PV 和 UV 的统计?

怎么计算在一个页面上的停留时间

  1. websocket,前端开个长连接,后台统计长连接时间。
  2. ajax 轮询,隔几秒发一个查询,后台记录第一与最后一个查询间隔时间。
  3. 关闭窗口或者跳转的时候会触发 window.onbeforeunload 函数,可以在该函数中做处理(有兼容性问题);统计完数据记录到本地 cookies 中,一段时间后统一发送

如何处理不让别人盗用你的图片,访问你的服务器资源

http header, 对 refer 做判断看来源是不是自己的网站,如果不是就拒绝 通过 session 校验,如果不通过特定服务生成 cookie 和 session 就不能请求得到资源

平台之前的对比和优劣

  1. sentry、aegis 等平台的有点和不足
  2. 如何进行弥补平台的不足,使得可以满足自己业务的需要

监控设计

  1. 如何计算白屏时间和首屏渲染时间的,如何进行数据上报的,上报到监控系统展示是怎样的一个过程

  2. 性能监控平台是如何捕获错误的

白屏时间和首屏时间的计算

首屏时间的计算方式

数据获取

浏览器渲染相关,如何做优化?

  1. 说到了 performance 的录制。 scripting 和 rendering
  2. 日志监控上报、不要影响渲染。
  3. 日常的业务中有没有关注过网站的性能指标,是否尝试过调研、接入开源的性能监控平台,是否了解性能监控 sdk 的一些原理
Last Updated:
Contributors: yiliang114